package com.haiyou.common.spring.schedule;

import java.io.Serializable;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.Map;

import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import com.google.common.collect.Maps;
import com.haiyou.common.thread.ThreadManager;
import com.haiyou.common.thread.ThreadManager.ThreadEnum;
import com.haiyou.common.thread.policy.DefaultRejectedPolicy;
import com.haiyou.common.util.time.ServerTime;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * @Description: 基于spring ThreadPoolTaskExecutor 实现
 * @author xingyuan
 * @date 2022年6月2日 下午5:40:55
 */
@Slf4j
final public class ScheduledExecutor {

	private static final class Lazy {
		private static final ScheduledExecutor instance;
		static {
			instance = new ScheduledExecutor();
		}
	}

	public static ScheduledExecutor getInstance() {
		return Lazy.instance;
	}

	private final ThreadPoolTaskScheduler scheduler;

	
	public ThreadPoolTaskScheduler getScheduler() {
		return scheduler;
	}

	private ScheduledExecutor() {

		int nThreads = ThreadManager.getThreadCount(ThreadEnum.scheduled_thread);
		
		log.info("========初始化定时器,线程数配置:{}========", nThreads);

		scheduler = new ThreadPoolTaskScheduler();
		// 设置核心线程数
		scheduler.setPoolSize(nThreads);

		// 设置后缀名称
		scheduler.setThreadNamePrefix(ScheduledExecutor.class.getSimpleName());

		// 设置拒绝策略
		scheduler.setRejectedExecutionHandler(DefaultRejectedPolicy.instance);

		// 取消的适合移除任务
		scheduler.setRemoveOnCancelPolicy(true);

		//等待和运行中的项目执行完成后 进程销毁
		scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(true);
		
		scheduler.initialize();
		
	}

	/**
	 * 停止
	 */
	public void shutdown() {
		scheduler.shutdown();
	}

	public <T> Future<T> submit(Callable<T> task) {
		return scheduler.submit(task);
	}

	public void execute(Runnable command) {
		scheduler.execute(command);
	}

	/**
	 * 延迟
	 * 
	 * @param command
	 * @param delay   延迟执行时间
	 * @param unit
	 * @return
	 */
	public RunnableScheduledTask schedule(RunnableScheduledTask task, long delay, TimeUnit unit) {

		Date startTime = new Date(unit.toMillis(delay) + ServerTime.timeMillis());
		
		ScheduledFuture<?> future = scheduler.schedule(task, startTime.toInstant());

		task.setFutrue(future);

		return task;
	}

	
	public ScheduledFuture<?>  schedule(Runnable task, long delay, TimeUnit unit) {

		Date startTime = new Date(unit.toMillis(delay) + ServerTime.timeMillis());

		return scheduler.schedule(task, startTime.toInstant());
	}

	public ScheduledFuture<?> schedule(Runnable task, Instant startTime){
		return scheduler.schedule(task,startTime);
	}

	public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task,long delay){
		return scheduler.scheduleWithFixedDelay(task,Duration.ofMillis(delay));
	}
	
	/**
	 * 基于 Trigger 定时器
	 * 
	 * @param task
	 * @param trigger
	 * @return
	 */
	public RunnableScheduledTask schedule(RunnableScheduledTask task, Trigger trigger) {
		ScheduledFuture<?> future = scheduler.schedule(task, trigger);
		task.setFutrue(future);
		return task;
	}

	public RunnableScheduledBossTask schedule(RunnableScheduledBossTask task, Trigger trigger) {
		ScheduledFuture<?> future = scheduler.schedule(task, trigger);
		task.setFutrue(future);
		return task;
	}
	
	
	private Map<String, ScheduledFuture<?>> map = Maps.newHashMap();
	
	
	

	/**
	 * scheduleAtFixedRate
	 * 
	 * @param command
	 * @param initialDelay 第一次执行时间
	 * @param period       间隔
	 * @param unit         时间单位
	 * @return
	 */
	public RunnableScheduledTask scheduleAtFixedRate(RunnableScheduledTask task, long initialDelay, long period,
			TimeUnit unit) {

		Date startTime = new Date(unit.toMillis(initialDelay) + ServerTime.timeMillis());

		period = unit.toMillis(period);

		ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(task, startTime.toInstant(), Duration.ofMillis(period));

		task.setFutrue(future);

		return task;
	}

	
	public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long period,
			TimeUnit unit) {

		Date startTime = new Date(unit.toMillis(initialDelay) + ServerTime.timeMillis());

		period = unit.toMillis(period);

		ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(task, startTime.toInstant(), Duration.ofMillis(period));
		
		return future;
	}
	
	
	/**
	 * scheduleWithFixedDelay
	 * 
	 * @param command
	 * @param initialDelay
	 * @param delay        延迟
	 * @param unit         时间单位
	 * @return
	 */
	public RunnableScheduledTask scheduleWithFixedDelay(RunnableScheduledTask task, long initialDelay, long delay,
			TimeUnit unit) {

		Date startTime = new Date(unit.toMillis(initialDelay) + ServerTime.timeMillis());

		delay = unit.toMillis(delay);

		ScheduledFuture<?> future = scheduler.scheduleWithFixedDelay(task,  startTime.toInstant(), Duration.ofMillis(delay));

		task.setFutrue(future);

		return task;
	}

	/**
	 * 延期执行 运行回调使用
	 * 
	 * @param <V>      回调对象
	 * @param callable 回调方法
	 * @param delay
	 * @param unit
	 * @return
	 */
	public <V extends Serializable> CallableScheduledTask<V> schedule(CallableScheduledTask<V> task, long delay,
			TimeUnit unit) {

		ScheduledFuture<V> future = scheduler.getScheduledExecutor().schedule(task, delay, unit);
		
		task.setFutrue(future);
		
		return task;
	}

}
